package com.ludomancy.hugealien;

import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.RenderingHints;
import java.awt.geom.QuadCurve2D;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.Vector;

import com.ludomancy.gamelib.Accumulator;
import com.ludomancy.gamelib.ImageStrip;
import com.ludomancy.gamelib.QuadCurve2DHelper;
import com.ludomancy.gamelib.Randomizer;
import com.ludomancy.gamelib.ResourceManager;
import com.ludomancy.gamelib.Utils;

public class City
{
	private class EntityComparator implements Comparator
	{
		public int compare(Object arg0, Object arg1)
		{
			BasicEntity e1 = (BasicEntity) arg0;
			BasicEntity e2 = (BasicEntity) arg1;
			
			int z1 = getOrder(e1);
			int z2 = getOrder(e2);
			return z1 - z2;
		}
		
		private int getOrder(BasicEntity e)
		{
			if(e instanceof Star || e instanceof Window)
				return 0;
			if(e instanceof Uruguayan)
				return 1;
			if(e instanceof Balloon)
				return 3;
			if(e instanceof RelationshipPainter)
				return 2;
			if(e instanceof Suicide)
				return 4;
			if(e instanceof Alien)
				return 5;
			return 2;
		}
	}

	public static final int TILE_WIDTH = 20;
	public static final int TILE_HEIGHT = 22;
	
	public static final int TYPE_BUILDING_BODY = 1;
	public static final int TYPE_BUILDING_ROOF = 2;

	public static final int TILE_BODY = 0;
	public static final int TILE_ROOF = 1;
	public static final int TILE_INHABITED_WINDOW = 3;
	public static final int TILE_BROKEN_WINDOW = 4;
	public static final int TILE_DARK_WINDOW = 2;
	
	private static final int MAX_BUILDING_HEIGHT_IN_TILES = 8;
	
	private int[][] map;
	private int width;
	private int height;
	
	public Vector entities = new Vector();
	private ImageStrip buildings;
	private int shortestBuilding = 10000;
	
	// for implementing relationship pulsing
	private Accumulator pulse = new Accumulator(1000);
		
	public City()
	{
		buildings = new ImageStrip(ResourceManager.getImage("buildings.png"), 20, 22);
	}
		
	public void paint(Graphics g, Rectangle view)
	{
		Utils.drawImage(g, ResourceManager.getImage("back.png"), 0, 0, 0);
				
		// paint buildings
		for(int y = 0 ; y != height ; y++)
		{
			for(int x = 0 ; x != width ; x++)
			{
				if(isIn(x, y))
				{
					int tile = map[x][y]; 
					switch(tile)
					{
						case TYPE_BUILDING_BODY:
						case TYPE_BUILDING_ROOF:
							buildings.paint(g, tile-1, x*TILE_WIDTH, y*TILE_HEIGHT);
							break;
					}
				}
			}
		}
				
		// paint me entities
		Vector toUpdate = (Vector) entities.clone();
		for (Iterator iterator = toUpdate.iterator(); iterator.hasNext();)
		{
			BasicEntity e = (BasicEntity) iterator.next();
			e.paint(g, e.x, e.y);
		}
		
		// repaint player above all
		Game.game.player.paintOverlay(g, 0, 0);
	}
	
	private QuadCurve2D ropeForRelationship(Uruguayan u1, Uruguayan u2, int offset)
	{
		int x1 = u1.x;
		int y1 = u1.y;
		int x2 = u2.x;
		int y2 = u2.y;
		
		// connect windows specially, based on height
		if ((y1 - y2) >= TILE_HEIGHT*2)
		{
			y1 -= 7;
			y2 += 5;
		}
		else
		if ((y2 - y1) >= TILE_HEIGHT*2)
		{
			y2 -= 7;
			y1 += 5;
		}
		
		if (x1 > x2)
		{
			x1 -= 6;
			x2 += 5;
		}
		else
		if (x1 < x2)
		{
			x1 += 6;
			x2 -= 5;
		}		
		
		int cx = (x1+x2)/2;
		int cy = (y1+y2)/2 + 32 + offset;
		return new QuadCurve2D.Float(x1, y1, cx, cy, x2, y2);
	}
	
	// helper vector with the new relationships scanned in getRelationshipsInArea
	private Vector newRelationships = new Vector();
	private Vector oldRelationships = new Vector();
		
	public Vector getRelationshipsInArea(Rectangle r)
	{
		newRelationships.clear();
		oldRelationships.clear();
		Vector rels = new Vector();
		for (int i = 0; i < entities.size(); i++)
		{
			if(entities.elementAt(i) instanceof Uruguayan)
			{
				Uruguayan u = (Uruguayan) entities.elementAt(i);
				Soul s1 = u.soul;
				for (int j = i+1; j < entities.size(); j++)
				{
					if(entities.elementAt(j) instanceof Uruguayan)
					{
						Uruguayan u2 = (Uruguayan) entities.elementAt(j);
						Soul s2 = u2.soul;
						if(s1.oldRelationships.contains(s2))
						{
							oldRelationships.add(new Uruguayan[]{u2,u});
						}
						if(s1.hasRelationship(s2))
						{
							if(	s1.newRelationships.contains(s2) && s2.newRelationships.contains(s1))
							{
								newRelationships.add(new Uruguayan[]{u2, u});
							}
							if(isRelationshipInArea(u, u2, r))
							{
								rels.add(new Uruguayan[]{u2, u});
							}
						}
					}
				}
			}
		}
		return rels;
	}
	
	private boolean isRelationshipInArea(Uruguayan u, Uruguayan u2, Rectangle r)
	{
		QuadCurve2D curve = ropeForRelationship(u, u2, 0);
		if(new QuadCurve2DHelper().curveIntersects(curve, r.x, r.y, r.width, r.height))
		{
			return true;
		}
		return false;
	}
	
	private boolean isInhabited(int tx, int ty)
	{
		for (int i = 0; i < entities.size(); i++)
		{
			BasicEntity be = (BasicEntity) entities.elementAt(i);
			if(be instanceof Uruguayan)
			{
				Uruguayan u = (Uruguayan) be;
				if(u.house.equals(new Point(tx,ty)))
				{
					return true;
				}
			}
		}
		return false;
	}

	public void update()
	{
		pulse.update();
		if(pulse.isDone())
		{
			pulse.reset();
		}
		for (int i = 0; i < entities.size(); i++)
		{
			BasicEntity be = (BasicEntity) entities.elementAt(i);
			be.update();
		}
		
		// sort the entities
		Collections.sort(entities, new EntityComparator());
		
	}
	
	public boolean isIn(int tx, int ty)
	{
		return tx >= 0 && ty >= 0 && tx < width && ty < height;
	}
		
	public static City generate()
	{
		// create map
		int cellW = Game.WORLD_WIDTH/TILE_WIDTH;
		int cellH = Game.WORLD_HEIGHT/TILE_HEIGHT + 1;
		int[][] map = new int[cellW][cellH];
		
		City c = new City();
		c.map = map;
		c.width = cellW;
		c.height = cellH;

		// make streets
		int[] streets = new int[0];
		for (int i = 0; i < streets.length; i++)
		{
			streets[i] = Randomizer.getRandom(2, cellW-4);
		}
		
		// make buildings
		int lastHeight = MAX_BUILDING_HEIGHT_IN_TILES - 3;
		for (int i = 0; i < cellW; i++)
		{
			boolean isStreet = false;
			for (int j = 0; j < streets.length; j++)
			{
				isStreet = isStreet || streets[j] == i || streets[j] == i+1;
			}
			
			if(!isStreet && i != cellW/2)
			{
				// decide height
				int height = -1;
				do
				{
					height = lastHeight + Randomizer.getRandom(-2,2);
					height = Math.min(MAX_BUILDING_HEIGHT_IN_TILES, height);
					height = Math.max(5, height);

				}while(lastHeight == height);
				if(height < c.shortestBuilding)
				{
					c.shortestBuilding = height;
				}
				lastHeight = height;
				// add the building
				for (int j = 1; j < height-1; j++)
				{
					map[i][cellH-j-1] = TYPE_BUILDING_BODY;
				}
				map[i][cellH-1] = TYPE_BUILDING_BODY;
				map[i][cellH-height] = TYPE_BUILDING_ROOF;
			}
		}
		
		// add relationship painter
		c.entities.add(new RelationshipPainter());
		
		return c;
	}

	public void placeSouls(Vector paramSouls)
	{
		// make sure the souls are randomly distributed
		Vector souls = (Vector) paramSouls.clone();
		Collections.shuffle(souls);
		int lastY = 0;
		for (int i = 0; i < souls.size(); i++)
		{
			Soul cur = (Soul) souls.elementAt(i);
			// place him in a house
			Point house = null;
			while(house == null)
			{
				int x = lessInhabitedBuilding();
				int y = Randomizer.getRandom(0, height-2);
				if(map[x][y] == TYPE_BUILDING_BODY || map[x][y] == TYPE_BUILDING_ROOF)
				{
					if(!isInhabited(x, y) && Math.abs(lastY-y) >= 2)
					{
						house = new Point(x,y);
						lastY = y;
					}
				}
			}
			entities.add(new Uruguayan(house, cur));		
		}
	}
	
//	private int lessInhabitedRow(int building)
//	{
//		int lessInhabited = Randomizer.getRandom(0, height-2);
//		int smallestInhabitants = 1000;
//		for (int i = 0; i < map[0].length; i++)
//		{
//			if(!isInhabited(building, i))
//			{
//				if(map[building][i] == TYPE_BUILDING_BODY || map[building][i] == TYPE_BUILDING_ROOF)
//				{
//					int amount = inhabitantsInRow(i);
//					if(amount < smallestInhabitants)
//					{
//						lessInhabited = i;
//						smallestInhabitants = amount;
//					}
//				}
//			}
//		}
//		return lessInhabited;
//	}
	
	private int lessInhabitedBuilding()
	{
		int lessInhabited = 0;
		int smallestInhabitants = 1000;
		for (int x = 0; x < map.length; x++)
		{
			if(isBuilding(x))
			{
				int curInhabitants = inhabitants(x); 
				if( curInhabitants < smallestInhabitants)
				{
					smallestInhabitants = curInhabitants;
					lessInhabited = x;
				}
			}
		}
		return lessInhabited;
	}
	
	private boolean isBuilding(int building)
	{
		return map[building][height-1] == TYPE_BUILDING_BODY;
	}
	
//	private int inhabitantsInRow(int building)
//	{
//		int c = 0;
//		for (int i = 0; i < entities.size(); i++)
//		{
//			BasicEntity be = (BasicEntity) entities.elementAt(i);
//			if(be instanceof Uruguayan)
//			{
//				Uruguayan u = (Uruguayan) be;
//				if(u.house.y == building)
//				{
//					c++;
//				}
//			}
//		}
//		return c;
//	}
	
	private int inhabitants(int building)
	{
		int c = 0;
		for (int i = 0; i < entities.size(); i++)
		{
			BasicEntity be = (BasicEntity) entities.elementAt(i);
			if(be instanceof Uruguayan)
			{
				Uruguayan u = (Uruguayan) be;
				if(u.house.x == building)
				{
					c++;
				}
			}
		}
		return c;
	}
	
	public boolean hasBuilding(int px, int py)
	{
		int tx = px/TILE_WIDTH;
		int ty = py/TILE_HEIGHT;
		if(isIn(tx,ty))
		{
			return map[tx][ty] != 0;
		}
		return false;
	}
	
	public int getSkyLine()
	{
		return  Game.WORLD_WIDTH - (MAX_BUILDING_HEIGHT_IN_TILES)*TILE_HEIGHT;
	}

	public void addStar(Star star)
	{
		Point pos = null;
		// place star
		while(pos == null)
		{
			int x = Randomizer.getRandom(star.area().width/2, Game.WORLD_WIDTH-star.area().width/2);
			int y = Randomizer.getRandom(star.area().height/2, Game.WORLD_WIDTH - shortestBuilding*TILE_HEIGHT);
			if(	!hasBuilding(x, y) && 
				!hasBuilding(x -star.area().width, y -star.area().height) &&
				!hasBuilding(x +star.area().width, y +star.area().height) &&
				unoccupied(star, star.area()))
			{
				pos = new Point(x,y);
			}
		}
		
		star.x = pos.x;
		star.y = pos.y;
		entities.add(star);
	}
	
	private boolean unoccupied(BasicEntity asker, Rectangle area)
	{
		for (int i = 0; i < entities.size(); i++)
		{
			BasicEntity be = (BasicEntity) entities.elementAt(i);
			if(be.area().intersects(area) && asker != be)
			{
				return false;
			}
		}
		return true;
	}

	public void killUruguayan(Uruguayan u)
	{
		entities.remove(u);
		entities.add(new Window(Window.TYPE_BROKEN, u.house.x*City.TILE_WIDTH, u.house.y*City.TILE_HEIGHT));
		// remove balloons, if the uruguayan is dead
		for (int i = 0; i < entities.size(); i++)
		{
			BasicEntity be = (BasicEntity) entities.elementAt(i);
			if(be instanceof Balloon)
			{
				Balloon b = (Balloon) be;
				if(b.uruguayan == u)
				{
					entities.removeElementAt(i);
					break;
				}
			}
		}
		Game.game.soulWorld.kill(u.soul);
	}

	public Uruguayan getUruguayan(Soul s)
	{
		for (int i = 0; i < entities.size(); i++)
		{
			BasicEntity be = (BasicEntity) entities.elementAt(i);
			if(be instanceof Uruguayan)
			{
				Uruguayan u = (Uruguayan) be;
				if(u.soul == s)
				{
					return u;
				}
			}
		}
		return null;
	}

	public void paintRelationships(Graphics g)
	{
		if(Game.game.isOver)
		{
			return;
		}
		Graphics2D g2 = (Graphics2D) g;
		Object oldAlias = g2.getRenderingHint(RenderingHints.KEY_ANTIALIASING);
		Object oldQuality = g2.getRenderingHint(RenderingHints.KEY_RENDERING);
		RenderingHints renderHints = new RenderingHints(
				RenderingHints.KEY_ANTIALIASING,
				RenderingHints.VALUE_ANTIALIAS_ON);
		renderHints.put(RenderingHints.KEY_RENDERING,
				RenderingHints.VALUE_RENDER_QUALITY);
		g2.addRenderingHints(renderHints);

		// paint relationships
		Graphics2D g2d = (Graphics2D) g;
		Utils.pushAlpha(g, 0.75f);
		Vector rels = getRelationshipsInArea(new Rectangle(0,0,Game.WORLD_WIDTH,Game.WORLD_HEIGHT));
		rels.addAll(newRelationships);
		for (int i = 0; i < rels.size(); i++)
		{
			Uruguayan[] rel = (Uruguayan[]) rels.elementAt(i);
			Uruguayan u1 = rel[0];
			Uruguayan u2 = rel[1];
			// signal uruguayans to show their mood
			u1.expressMood();
			u2.expressMood();
			float stroke = 1.5f;
			g2d.setStroke(new BasicStroke(stroke));
			g.setColor(new Color(0xdddddd));
			// draw the ropes
			int movement = 0;
			Color special = null;
			// move it slightly if it is on the scissor area
			if(isRelationshipInArea(u1, u2, Game.game.player.scissorArea()))
			{
				movement = -5;
//				special = new Color(0xff8e8e);
			}
			QuadCurve2D curve = ropeForRelationship(u1, u2, movement);
			if(newRelationships.contains(rel))
			{
				Utils.pushAlpha(g, 1.0f);
				g.setColor(new Color(0xfefefe));
				g2d.setStroke(new BasicStroke(2.0f));
				if(special != null)
				{
					g.setColor(special);
				}
				g2d.draw(curve);
				Utils.popAlpha(g);
			}
			else
			{
				if(special != null)
				{
					g.setColor(special);
				}
				g2d.draw(curve);
			}
		}
//		Utils.pushAlpha(g, 0.2f);
//		for (int i = 0; i < oldRelationships.size(); i++)
//		{
//			Uruguayan[] rel = (Uruguayan[]) oldRelationships.elementAt(i);
//			Uruguayan u1 = rel[0];
//			Uruguayan u2 = rel[1];
//			BasicStroke stroke = new BasicStroke(2.0f, BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL, 0, new float[]{1.0f}, 0);
//			g2d.setStroke(stroke);
//			g.setColor(new Color(0x2F2F2F));
//			QuadCurve2D curve = ropeForRelationship(u1, u2, 0);
//			g2d.draw(curve);
//		}
//		Utils.popAlpha(g);
		Utils.popAlpha(g);
		g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, oldAlias);
		g2.setRenderingHint(RenderingHints.KEY_RENDERING, oldQuality);
	}

}
